home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Code Resources / Eclectic CDEFs / CDEFTester Folder / MovableModal.p < prev   
Text File  |  1997-02-27  |  13KB  |  445 lines

  1. {    MovableModal: This unit implements a MovableModalDialog routine similar to the Toolbox routine ModalDialog    }
  2. {    to be used for movable modal dialogs    }
  3. {}
  4. {    ANTI© 1993 Merzwaren, modified by Sebastiano Pilla 1996    }
  5.  
  6. {    <mailto:case@tvol.it>    }
  7.  
  8. unit MovableModal;
  9.  
  10.  
  11. interface
  12.  
  13.  
  14.     uses
  15.         Dialogs;
  16.  
  17.  
  18.     procedure DisableMenuBar (inEditMenuID: SInt16;
  19.                                     inHmnuID: SInt16);
  20.  
  21.     procedure ReEnableMenuBar;
  22.  
  23.     procedure MovableModalDialog (inFilterUPP: ModalFilterUPP;
  24.                                     var ioItemHit: SInt16);
  25.  
  26.  
  27. implementation
  28.  
  29.  
  30.     uses
  31.         Balloons, LowMem;
  32.  
  33.  
  34.     const
  35.         kSystemMenuThreshold = -16000;        { menu IDs <= than this are used by the system }
  36.         kMovableModalEventMask = mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask;
  37.         kCutKeyEquiv = 'X';
  38.         kCopyKeyEquiv = 'C';
  39.         kPasteKeyEquiv = 'V';
  40.  
  41.  
  42.     type
  43.         MenuEntry = record
  44.                 hMenu: MenuHandle;
  45.                 leftEdge: SInt16;
  46.             end;
  47.  
  48.  
  49.         MenuList = record
  50.                 offsetToLastMenu: SInt16;
  51.                 rightmostEdge: SInt16;
  52.                 unused: SInt16;
  53.                 theMenus: array[0..100] of MenuEntry;
  54.             end;
  55.         MenuListPtr = ^MenuList;
  56.         MenuListH = ^MenuListPtr;
  57.  
  58.  
  59.         MenuBarState = record
  60.                 mbsBarEnable: UInt32;
  61.                 mbsEditEnable: UInt32;
  62.                 mbsEditMenuID: SInt16;
  63.                 mbsCutItem: SInt16;
  64.                 mbsCopyItem: SInt16;
  65.                 mbsPasteItem: SInt16;
  66.             end;
  67.         MenuBarStatePtr = ^MenuBarState;
  68.         MenuBarStateH = ^MenuBarStatePtr;
  69.  
  70.  
  71.     var
  72.         pSaveStateHdl: Handle;
  73.  
  74.  
  75. {    GetMenuItemFromKeyEquiv    }
  76. {}
  77. {    Returns the item number of the menu item with the given char    }
  78. {}
  79. {    Entry:    inMenu = handle to menu    }
  80. {            inKeyEquiv = key equivalent    }
  81. {    Exit:    function result = item number requested    }
  82.     function GetMenuItemFromKeyEquiv (inMenu: MenuHandle;
  83.                                     inKeyEquiv: Char): SInt16;
  84.         var
  85.             item, nItems: SInt16;
  86.             cmd: Char;
  87.     begin
  88.         GetMenuItemFromKeyEquiv := 0;
  89.         nItems := CountMItems(inMenu);
  90.         for item := 1 to nItems do
  91.             begin
  92.                 GetItemCmd(inMenu, item, cmd);
  93.                 if (Ord(cmd) = Ord(inKeyEquiv)) then
  94.                     begin
  95.                         GetMenuItemFromKeyEquiv := item;
  96.                         Exit(GetMenuItemFromKeyEquiv);
  97.                     end;
  98.             end;
  99.     end;
  100.  
  101.  
  102. {    DisableMenuBar    }
  103. {}
  104. {    Disables the entire menu bar, excluding the Edit & System menus    }
  105. {}
  106. {    Entry:    inEditMenuID = resource ID of Edit menu (or 0 if no Edit menu)    }
  107. {            inHmnuID = resource ID of valid 'hmnu' resource (or -1 if no string remapping desired)    }
  108. {    Note: Call this routine soon after putting up the movable modal, so the frontmost window is assured    }
  109. {    to be the dialog    }
  110.     procedure DisableMenuBar (inEditMenuID: SInt16;
  111.                                     inHmnuID: SInt16);
  112.         var
  113.             menuList: MenuListH;
  114.             theMenu: MenuHandle;
  115.             menuEnable: UInt32;
  116.             theDialog: DialogPtr;
  117.             barEnable, response: SInt32;
  118.             i, nMenus, menuID: SInt16;
  119.             err: OSErr;
  120.             hasBalloons, needEditMenu: Boolean;
  121.     begin
  122.         hasBalloons := (Gestalt(gestaltHelpMgrAttr, response) = noErr) & BTST(response, gestaltHelpMgrPresent);
  123.  
  124.     { determine if the frontmost dialog contains edit fields }
  125.         theDialog := FrontWindow;
  126.         needEditMenu := (theDialog <> nil) & (DialogPeek(theDialog)^.editField >= 0);
  127.  
  128.     { get a handle to the menu list and count the menus }
  129.         menuList := MenuListH(LMGetMenuList);
  130.         nMenus := menuList^^.offsetToLastMenu div SizeOf(MenuEntry);
  131.  
  132.     { create a parameter block for saving menu bar state information }
  133.         pSaveStateHdl := NewHandleClear(SizeOf(MenuBarState));
  134.         if pSaveStateHdl <> nil then
  135.             HLock(pSaveStateHdl);
  136.         if MemError = noErr then
  137.             with MenuBarStateH(pSaveStateHdl)^^ do
  138.                 begin
  139.                     barEnable := 0;
  140.  
  141.             { walk the menu list }
  142.                     for i := 0 to nMenus - 1 do
  143.                         begin
  144.  
  145.                 { get menu handle, menu ID and enable flags for this menu }
  146.                             theMenu := menuList^^.theMenus[i].hMenu;
  147.                             menuID := theMenu^^.menuID;
  148.                             menuEnable := theMenu^^.enableFlags;
  149.  
  150.                 { do nothing if this is a system menu }
  151.                             if (menuID <= kSystemMenuThreshold) then
  152.                                 Cycle;
  153.  
  154.                 { if this is the Edit menu and we need it, do some special processing }
  155.                             if ((needEditMenu) and (menuID = inEditMenuID)) then
  156.                                 begin
  157.  
  158.                     { save edit menu ID }
  159.                                     mbsEditMenuID := inEditMenuID;
  160.  
  161.                     { save the enable flags for later restoration }
  162.                                     mbsEditEnable := menuEnable;
  163.  
  164.                     { find which items are Cut, Copy and Paste }
  165.                                     mbsCutItem := GetMenuItemFromKeyEquiv(theMenu, kCutKeyEquiv);
  166.                                     mbsCopyItem := GetMenuItemFromKeyEquiv(theMenu, kCopyKeyEquiv);
  167.                                     mbsPasteItem := GetMenuItemFromKeyEquiv(theMenu, kPasteKeyEquiv);
  168.  
  169.                     { enable Cut, Copy and Paste }
  170.                                     menuEnable := 1 + BSL(1, mbsCutItem) + BSL(1, mbsCopyItem) + BSL(1, mbsPasteItem);
  171.                                     theMenu^^.enableFlags := menuEnable;
  172.  
  173.                                     Cycle;
  174.                                 end;
  175.  
  176.                 { if this menu is enabled, disable it and set the corresponding bit in barEnable }
  177.                             if (BTST(menuEnable, 0)) then
  178.                                 begin
  179.                                     barEnable := BOR(barEnable, BSL(1, i));
  180.                                     DisableItem(theMenu, 0);
  181.                                 end;
  182.  
  183.                 { remap the help strings for this menu }
  184.                             if (hasBalloons) then
  185.                                 err := HMSetMenuResID(menuID, inHmnuID);
  186.                         end;
  187.                     mbsBarEnable := barEnable;
  188.                 end;
  189.  
  190.     { unhighlight the highlighted menu (if any) and redraw the menu bar }
  191.         HiliteMenu(0);
  192.         DrawMenuBar;
  193.  
  194.     { unlock state info parameter block }
  195.         HUnlock(pSaveStateHdl);
  196.     end;
  197.  
  198.  
  199. {    ReEnableMenuBar    }
  200. {}
  201. {    Restores the previously saved enableFlags for all menus in the menu bar    }
  202. {}
  203. {    Note: Call this routine just after bringing down the movable modal    }
  204.     procedure ReEnableMenuBar;
  205.         var
  206.             menuList: MenuListH;
  207.             response: SInt32;
  208.             theMenu: MenuHandle;
  209.             i, nMenus, menuID: SInt16;
  210.             hasBalloons: Boolean;
  211.             err: OSErr;
  212.     begin
  213.     { sanity check: make sure pSaveStateHdl isn't NIL }
  214.         if (pSaveStateHdl = nil) then
  215.             Exit(ReEnableMenuBar);
  216.  
  217.     { determine if the Help manager is available }
  218.         hasBalloons := (Gestalt(gestaltHelpMgrAttr, response) = noErr) & BTST(response, gestaltHelpMgrPresent);
  219.  
  220.     { get a handle to the menu list and count the menus }
  221.         menuList := MenuListH(LMGetMenuList);
  222.         nMenus := menuList^^.offsetToLastMenu div SizeOf(MenuEntry);
  223.  
  224.         HLock(pSaveStateHdl);
  225.         if MemError = noErr then
  226.             with MenuBarStateH(pSaveStateHdl)^^ do
  227.                 begin
  228.  
  229.             { walk the menu list }
  230.                     for i := 0 to nMenus - 1 do
  231.                         begin
  232.  
  233.                 { get menu handle and menu ID for this menu }
  234.                             theMenu := menuList^^.theMenus[i].hMenu;
  235.                             menuID := theMenu^^.menuID;
  236.  
  237.                 { do nothing if this is a system menu }
  238.                             if (menuID <= kSystemMenuThreshold) then
  239.                                 Cycle;
  240.  
  241.                 { restore old enable state for this menu }
  242.                             if (menuID = mbsEditMenuID) then
  243.                                 theMenu^^.enableFlags := mbsEditEnable
  244.                             else if (BTST(mbsBarEnable, i)) then
  245.                                 EnableItem(theMenu, 0);
  246.  
  247.                 { unmap the help strings for this menu }
  248.                             if (hasBalloons) then
  249.                                 err := HMSetMenuResID(menuID, -1);
  250.                         end;
  251.                 end;
  252.  
  253.     { forget about the menu bar parameter block }
  254.         HUnlock(pSaveStateHdl);
  255.         if pSaveStateHdl <> nil then
  256.             DisposeHandle(pSaveStateHdl);
  257.  
  258.     { redraw the menu bar }
  259.         DrawMenuBar;
  260.     end;
  261.  
  262.  
  263. {    CallBeeper    }
  264. {}
  265. {    Calls the beeper procedure with the given sound    }
  266. {}
  267. {    Entry:    soundNo = ID of 'snd ' resource to play    }
  268. {            beeperProc = pointer to beeper proc    }
  269.     procedure CallBeeper (soundNo: SInt16;
  270.                                     beeperProc: ProcPtr);
  271.     inline
  272.         $205F,        {    movea.l    (sp)+, a0    }
  273.         $4E90;        {    jsr            (a0)        }
  274.  
  275.  
  276. {    MovableModalMenuChoice    }
  277. {}
  278. {    Called by the filter proc to process menu choices    }
  279. {}
  280. {    Entry:    inDialog = pointer to current dialog    }
  281. {            inEvent = ignored    }
  282. {            inMenuChoice = menu choice combination    }
  283. {    Exit:    outItemHit = item number of edit field interested    }
  284. {            function result = TRUE if edit operation performed, FALSE otherwise    }
  285.     function MovableModalMenuChoice (inDialog: DialogPtr;
  286.                                     var inEvent: EventRecord;
  287.                                     var outItemHit: SInt16;
  288.                                     inMenuChoice: SInt32): Boolean;
  289.         var
  290.             itemRect: Rect;
  291.             itemHandle: Handle;
  292.             menuID, menuItem, currentEditField, itemType: SInt16;
  293.             err: OSErr;
  294.     begin
  295.         MovableModalMenuChoice := false;
  296.         if (pSaveStateHdl = nil) then
  297.             Exit(MovableModalMenuChoice);
  298.         menuID := HiWrd(inMenuChoice);
  299.         menuItem := LoWrd(inMenuChoice);
  300.         HLock(pSaveStateHdl);
  301.         if MemError = noErr then
  302.             with MenuBarStateH(pSaveStateHdl)^^ do
  303.                 begin
  304.                     if (menuID = mbsEditMenuID) then
  305.                         begin
  306.  
  307.                 { find the current edit field }
  308.                             currentEditField := DialogPeek(inDialog)^.editField + 1;
  309.                             GetDialogItem(inDialog, currentEditField, itemType, itemHandle, itemRect);
  310.  
  311.                 { if the current edit field is an enabled item, exit from MovableModalDialog loop }
  312.                             if (BAND(itemType, kItemDisableBit) = 0) then
  313.                                 begin
  314.                                     MovableModalMenuChoice := true;
  315.                                     outItemHit := currentEditField;
  316.                                 end;
  317.  
  318.                 { perform edit operation }
  319.                             if (menuItem = mbsCutItem) then
  320.                                 begin
  321.                                     DialogCut(inDialog);
  322.                                     err := ZeroScrap;
  323.                                     err := TEToScrap;
  324.                                 end
  325.                             else if (menuItem = mbsCopyItem) then
  326.                                 begin
  327.                                     DialogCopy(inDialog);
  328.                                     err := ZeroScrap;
  329.                                     err := TEToScrap;
  330.                                 end
  331.                             else if (menuItem = mbsPasteItem) then
  332.                                 DialogPaste(inDialog);
  333.                         end;
  334.                 end;
  335.         HUnlock(pSaveStateHdl);
  336.         HiliteMenu(0);
  337.     end;
  338.  
  339.  
  340. {    HandleMovableModalMouseDown    }
  341. {}
  342. {    Handles mouse down events for the filter proc    }
  343. {}
  344. {    Entry:    inDialog = pointer to dialog    }
  345. {            ioEvent = event specification    }
  346. {            ioItemHit = item number of dialog item interested    }
  347. {    Exit:    ioEvent = event spec after executing    }
  348. {            ioItemHit = item number of edit field interested    }
  349. {            function result = TRUE if the filter proc should exit, FALSE otherwise    }
  350.     function HandleMovableModalMouseDown (inDialog: DialogPtr;
  351.                                     var ioEvent: EventRecord;
  352.                                     var ioItemHit: SInt16): Boolean;
  353.         var
  354.             dragRect: Rect;
  355.             wind: WindowPtr;
  356.             beeper: UniversalProcPtr;
  357.             partCode: SInt16;
  358.     begin
  359.         HandleMovableModalMouseDown := false;
  360.  
  361.     { find out where the click went down in }
  362.         partCode := FindWindow(ioEvent.where, wind);
  363.  
  364.     { if the click went in the menu bar, just call MenuSelect }
  365.         if (partCode = inMenuBar) then
  366.             begin
  367.                 HandleMovableModalMouseDown := MovableModalMenuChoice(inDialog, ioEvent, ioItemHit, MenuSelect(ioEvent.where));
  368.                 Exit(HandleMovableModalMouseDown);
  369.             end;
  370.  
  371.     { if the user clicked somewhere outside the dialog but in one of our windows behind, call the beeper }
  372.         if not ((partCode = inDesk) or (partCode = inSysWindow)) then
  373.             if (not PtInRgn(ioEvent.where, WindowPeek(inDialog)^.strucRgn)) then
  374.                 begin
  375.                     beeper := LMGetDABeeper;
  376.                     if (beeper <> nil) then
  377.                         CallBeeper(1, beeper);
  378.                     Exit(HandleMovableModalMouseDown);
  379.                 end;
  380.  
  381.     { now, we have to handle the only thing DialogSelect doesn't do for us: dragging }
  382.         if (partCode = inDrag) & (inDialog = wind) then
  383.             begin
  384.                 dragRect := GetGrayRgn^^.rgnBBox;
  385.                 DragWindow(wind, ioEvent.where, dragRect);
  386.                 ioEvent.what := nullEvent;
  387.             end;
  388.     end;
  389.  
  390.  
  391. {    MovableModalDialog    }
  392. {}
  393. {    Substitutes ModalDialog for movable modal dialogs    }
  394. {}
  395. {    Entry:    inFilterUPP = same as modalFilter parameter for ModalDialog    }
  396. {            ioItemHit = same as itemHit parameter for ModalDialog    }
  397. {    Exit:    ioItemHit = same as itemHit parameter for ModalDialog    }
  398.     procedure MovableModalDialog (inFilterUPP: ModalFilterUPP;
  399.                                     var ioItemHit: SInt16);
  400.         var
  401.             theEvent: EventRecord;
  402.             savePort: GrafPtr;
  403.             theDialog: DialogPtr;
  404.             gotEvent: Boolean;
  405.     begin
  406.         ioItemHit := 0;
  407.  
  408.     { get a pointer to the frontmost window (which should be our movable dialog) }
  409.         theDialog := FrontWindow;
  410.         if (theDialog = nil) then
  411.             Exit(MovableModalDialog);
  412.  
  413.     { set thePort to the dialog }
  414.         GetPort(savePort);
  415.         SetPort(theDialog);
  416.  
  417.     { modal dialog event loop }
  418.         repeat
  419.  
  420.         { yield time to other processes and retrieve next event from the queue }
  421.             gotEvent := WaitNextEvent(kMovableModalEventMask, theEvent, GetCaretTime, nil);
  422.  
  423.         { the filter proc is the first one to get a chance to process the event }
  424.             if (inFilterUPP <> nil) & CallModalFilterProc(theDialog, theEvent, ioItemHit, inFilterUPP) then
  425.                 Leave;
  426.  
  427.         { then comes our own processing of clicks in the menu bar and in the drag bar }
  428.             if (theEvent.what = mouseDown) & HandleMovableModalMouseDown(theDialog, theEvent, ioItemHit) then
  429.                 Leave;
  430.  
  431.         { and our processing of keyboard equivalents for the Edit items }
  432.             if (theEvent.what = keyDown) & (BAND(theEvent.modifiers, cmdKey) <> 0) & MovableModalMenuChoice(theDialog, theEvent, ioItemHit, MenuKey(CHR(BAND(theEvent.message, charCodeMask)))) then
  433.                 Leave;
  434.  
  435.         { finally we let the Toolbox do its own thing }
  436.             if IsDialogEvent(theEvent) & DialogSelect(theEvent, theDialog, ioItemHit) then
  437.                 Leave;
  438.         until false;
  439.  
  440.     { restore the old port }
  441.         SetPort(savePort);
  442.     end;
  443.  
  444.  
  445. end.